/**
 * 文件上传组件
 * 支持小文件直接上传和大文件分片上传，断点续传等功能
 */
class FileUploadComponent {
    constructor(options = {}) {
        // 配置选项
        this.config = {
            baseUrl: options.baseUrl || 'http://localhost:8080',
            apiPrefix: options.apiPrefix || '/file/upload',
            maxSmallFileSize: options.maxSmallFileSize || 50 * 1024 * 1024, // 50MB
            defaultChunkSize: options.defaultChunkSize || 5 * 1024 * 1024, // 5MB
            maxConcurrency: options.maxConcurrency || 3, // 最大并发数
            autoRetry: options.autoRetry !== false, // 默认开启自动重试
            maxRetries: options.maxRetries || 3,
            progressPollingInterval: options.progressPollingInterval || 2000, // 进度查询间隔
            onProgress: options.onProgress || null,
            onSuccess: options.onSuccess || null,
            onError: options.onError || null,
            onStatusChange: options.onStatusChange || null
        };

        // 内部状态
        this.currentUploadId = null;
        this.currentFile = null;
        this.totalChunks = 0;
        this.uploadedChunks = new Set();
        this.isUploading = false;
        this.isPaused = false;
        this.chunks = [];
        this.currentChunkIndex = 0;
        this.progressTimer = null;
        this.retryTracker = new Map();

        // 重试配置
        this.RETRY_CONFIG = {
            maxRetries: this.config.maxRetries,
            baseDelay: 1000,
            maxDelay: 8000,
            backoffFactor: 2
        };
    }

    /**
     * 创建文件上传按钮
     * @param {HTMLElement} container 容器元素
     * @param {Object} options 选项
     */
    createUploadButton(container, options = {}) {
        const {
            buttonText = '选择文件上传',
            multiple = false,
            accept = '*/*',
            category = 'default',
            chunkSize = this.config.defaultChunkSize,
            showProgress = true,
            showPreview = false
        } = options;

        // 创建HTML结构
        const uploadWrapper = document.createElement('div');
        uploadWrapper.className = 'file-upload-wrapper';
        uploadWrapper.innerHTML = `
            <style>
                .file-upload-wrapper {
                    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                    max-width: 600px;
                }
                .file-upload-button {
                    display: inline-block;
                    padding: 12px 24px;
                    background: #007bff;
                    color: white;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    font-size: 14px;
                    transition: background 0.3s;
                }
                .file-upload-button:hover {
                    background: #0056b3;
                }
                .file-upload-button:disabled {
                    background: #ccc;
                    cursor: not-allowed;
                }
                .file-upload-input {
                    display: none;
                }
                .upload-progress {
                    margin: 15px 0;
                    display: none;
                }
                .progress-bar {
                    width: 100%;
                    height: 20px;
                    background: #f0f0f0;
                    border-radius: 10px;
                    overflow: hidden;
                    margin: 10px 0;
                }
                .progress-fill {
                    height: 100%;
                    background: linear-gradient(90deg, #007bff, #0056b3);
                    border-radius: 10px;
                    transition: width 0.3s ease;
                    width: 0%;
                }
                .upload-info {
                    background: #f8f9fa;
                    padding: 15px;
                    border-radius: 6px;
                    margin: 15px 0;
                    border-left: 4px solid #007bff;
                    display: none;
                }
                .upload-controls {
                    margin: 10px 0;
                    display: none;
                }
                .control-button {
                    padding: 8px 16px;
                    margin: 0 5px 5px 0;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                }
                .btn-pause { background: #ffc107; color: #212529; }
                .btn-resume { background: #28a745; color: white; }
                .btn-cancel { background: #dc3545; color: white; }
                .btn-retry { background: #17a2b8; color: white; }
                .file-preview {
                    margin: 10px 0;
                    display: none;
                }
                .file-item {
                    background: #f8f9fa;
                    padding: 10px;
                    border-radius: 4px;
                    margin: 5px 0;
                    border: 1px solid #dee2e6;
                }
                .status-uploading { border-left: 4px solid #ffc107; }
                .status-completed { border-left: 4px solid #28a745; }
                .status-error { border-left: 4px solid #dc3545; }
                .status-paused { border-left: 4px solid #6c757d; }
            </style>
            
            <button class="file-upload-button" id="uploadBtn">
                ${buttonText}
            </button>
            <input type="file" class="file-upload-input" id="fileInput" 
                   ${multiple ? 'multiple' : ''} accept="${accept}">
            
            ${showPreview ? '<div class="file-preview" id="filePreview"></div>' : ''}
            
            ${showProgress ? `
                <div class="upload-progress" id="uploadProgress">
                    <div class="progress-bar">
                        <div class="progress-fill" id="progressFill"></div>
                    </div>
                    <div id="progressText">0%</div>
                </div>
                
                <div class="upload-controls" id="uploadControls">
                    <button class="control-button btn-pause" id="pauseBtn">暂停</button>
                    <button class="control-button btn-resume" id="resumeBtn" disabled>继续</button>
                    <button class="control-button btn-cancel" id="cancelBtn">取消</button>
                    <button class="control-button btn-retry" id="retryBtn" disabled>重试</button>
                </div>
            ` : ''}
            
            <div class="upload-info" id="uploadInfo"></div>
        `;

        container.appendChild(uploadWrapper);

        // 绑定事件
        this._bindEvents(uploadWrapper, {
            category,
            chunkSize,
            showProgress,
            showPreview
        });

        return uploadWrapper;
    }

    /**
     * 绑定事件
     */
    _bindEvents(wrapper, options) {
        const uploadBtn = wrapper.querySelector('#uploadBtn');
        const fileInput = wrapper.querySelector('#fileInput');
        const pauseBtn = wrapper.querySelector('#pauseBtn');
        const resumeBtn = wrapper.querySelector('#resumeBtn');
        const cancelBtn = wrapper.querySelector('#cancelBtn');
        const retryBtn = wrapper.querySelector('#retryBtn');

        // 点击按钮选择文件
        uploadBtn.addEventListener('click', () => {
            fileInput.click();
        });

        // 文件选择
        fileInput.addEventListener('change', (e) => {
            const files = Array.from(e.target.files);
            if (files.length > 0) {
                this._handleFileSelection(files, wrapper, options);
            }
        });

        // 控制按钮
        if (pauseBtn) {
            pauseBtn.addEventListener('click', () => this.pauseUpload());
        }
        if (resumeBtn) {
            resumeBtn.addEventListener('click', () => this.resumeUpload());
        }
        if (cancelBtn) {
            cancelBtn.addEventListener('click', () => this.cancelUpload());
        }
        if (retryBtn) {
            retryBtn.addEventListener('click', () => this.retryUpload());
        }
    }

    /**
     * 处理文件选择
     */
    async _handleFileSelection(files, wrapper, options) {
        const { category, chunkSize, showProgress, showPreview } = options;

        // 显示文件预览
        if (showPreview) {
            this._showFilePreview(files, wrapper);
        }

        // 上传文件
        for (const file of files) {
            try {
                await this.uploadFile(file, {
                    category,
                    chunkSize,
                    wrapper
                });
            } catch (error) {
                this._showError(`文件 ${file.name} 上传失败: ${error.message}`, wrapper);
            }
        }
    }

    /**
     * 显示文件预览
     */
    _showFilePreview(files, wrapper) {
        const preview = wrapper.querySelector('#filePreview');
        if (!preview) return;

        preview.innerHTML = '';
        preview.style.display = 'block';

        files.forEach(file => {
            const fileItem = document.createElement('div');
            fileItem.className = 'file-item';
            fileItem.innerHTML = `
                <strong>${file.name}</strong><br>
                大小: ${this._formatFileSize(file.size)}<br>
                类型: ${file.type || '未知'}
            `;
            preview.appendChild(fileItem);
        });
    }

    /**
     * 上传文件（主入口）
     * @param {File} file 文件对象
     * @param {Object} options 上传选项
     */
    async uploadFile(file, options = {}) {
        const {
            category = 'default',
            chunkSize = this.config.defaultChunkSize,
            wrapper = null
        } = options;

        this.currentFile = file;
        this._updateStatus('preparing', wrapper);

        try {
            // 判断是否使用分片上传
            if (file.size <= this.config.maxSmallFileSize) {
                return await this._uploadSmallFile(file, category, wrapper);
            } else {
                return await this._uploadLargeFile(file, category, chunkSize, wrapper);
            }
        } catch (error) {
            this._updateStatus('error', wrapper);
            this._triggerCallback('onError', error);
            throw error;
        }
    }

    /**
     * 小文件直接上传
     */
    async _uploadSmallFile(file, category, wrapper) {
        this._updateStatus('uploading', wrapper);
        this._showControls(wrapper, false);

        const formData = new FormData();
        formData.append('file', file);
        formData.append('category', category);

        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();

            // 进度监听
            xhr.upload.addEventListener('progress', (e) => {
                if (e.lengthComputable) {
                    const progress = Math.round((e.loaded / e.total) * 100);
                    this._updateProgress(progress, wrapper);
                    this._triggerCallback('onProgress', {
                        progress,
                        loaded: e.loaded,
                        total: e.total,
                        type: 'small'
                    });
                }
            });

            xhr.onload = () => {
                if (xhr.status === 200) {
                    const response = JSON.parse(xhr.responseText);
                    if (response.code === 200) {
                        this._updateStatus('completed', wrapper);
                        this._showResult(response.data, wrapper);
                        this._triggerCallback('onSuccess', response.data);
                        resolve(response.data);
                    } else {
                        this._updateStatus('error', wrapper);
                        this._showError(response.message, wrapper);
                        reject(new Error(response.message));
                    }
                } else {
                    this._updateStatus('error', wrapper);
                    reject(new Error(`HTTP ${xhr.status}`));
                }
            };

            xhr.onerror = () => {
                this._updateStatus('error', wrapper);
                reject(new Error('网络错误'));
            };

            xhr.open('POST', `${this.config.baseUrl}${this.config.apiPrefix}`);
            xhr.send(formData);
        });
    }

    /**
     * 大文件分片上传
     */
    async _uploadLargeFile(file, category, chunkSize, wrapper) {
        this._updateStatus('preparing', wrapper);

        // 计算文件MD5
        const fileMD5 = await this._calculateFileMD5(file);

        // 切分文件
        this.totalChunks = Math.ceil(file.size / chunkSize);
        this.chunks = [];
        for (let i = 0; i < this.totalChunks; i++) {
            const start = i * chunkSize;
            const end = Math.min(start + chunkSize, file.size);
            this.chunks.push(file.slice(start, end));
        }

        // 初始化分片上传
        const initRequest = {
            filename: file.name,
            fileSize: file.size,
            fileMd5: fileMD5,
            chunkSize: chunkSize,
            category: category
        };

        const initResponse = await fetch(`${this.config.baseUrl}${this.config.apiPrefix}/init`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(initRequest)
        });

        const initResult = await initResponse.json();
        if (initResult.code !== 200) {
            throw new Error(initResult.message);
        }

        const initData = initResult.data;
        this.currentUploadId = initData.uploadId;
        this.uploadedChunks.clear();

        if (initData.isDuplicate) {
            this._updateStatus('completed', wrapper);
            this._showResult(initData, wrapper);
            this._triggerCallback('onSuccess', initData);
            return initData;
        }

        // 开始分片上传
        this._updateStatus('uploading', wrapper);
        this._showControls(wrapper, true);

        return await this._startChunkUpload(wrapper);
    }

    /**
     * 开始分片上传
     */
    async _startChunkUpload(wrapper) {
        this.isUploading = true;
        this.isPaused = false;
        this.currentChunkIndex = 0;

        this._startProgressPolling();

        return new Promise((resolve, reject) => {
            const upload = async () => {
                const maxConcurrency = this.config.maxConcurrency;
                let activeUploads = 0;
                let hasError = false;

                const uploadNext = async () => {
                    while (this.currentChunkIndex < this.totalChunks &&
                        activeUploads < maxConcurrency &&
                        !this.isPaused &&
                        !hasError) {

                        const chunkIndex = this.currentChunkIndex;
                        const chunkNumber = chunkIndex + 1;

                        // 跳过已上传的分片
                        if (this.uploadedChunks.has(chunkNumber)) {
                            this.currentChunkIndex++;
                            continue;
                        }

                        activeUploads++;
                        this.currentChunkIndex++;

                        try {
                            const success = await this._uploadSingleChunk(chunkIndex);
                            activeUploads--;

                            if (success) {
                                this._updateChunkProgress(wrapper);

                                // 检查是否所有分片都完成
                                if (this.uploadedChunks.size === this.totalChunks) {
                                    const result = await this._completeUpload();
                                    this._updateStatus('completed', wrapper);
                                    this._showResult(result, wrapper);
                                    this._triggerCallback('onSuccess', result);
                                    resolve(result);
                                    return;
                                }

                                // 继续上传
                                uploadNext();
                            } else {
                                hasError = true;
                                this._updateStatus('error', wrapper);
                                reject(new Error('分片上传失败'));
                            }
                        } catch (error) {
                            activeUploads--;
                            hasError = true;
                            this._updateStatus('error', wrapper);
                            reject(error);
                        }
                    }
                };

                uploadNext();
            };

            upload();
        });
    }

    /**
     * 上传单个分片
     */
    async _uploadSingleChunk(chunkIndex) {
        const chunk = this.chunks[chunkIndex];
        const chunkNumber = chunkIndex + 1;

        try {
            const chunkMD5 = await this._calculateChunkMD5(chunk);

            const formData = new FormData();
            formData.append('uploadId', this.currentUploadId);
            formData.append('chunkNumber', chunkNumber);
            formData.append('chunk', chunk);
            formData.append('chunkMd5', chunkMD5);

            const response = await fetch(`${this.config.baseUrl}${this.config.apiPrefix}/chunk`, {
                method: 'POST',
                body: formData
            });

            const result = await response.json();
            if (result.code === 200) {
                const chunkData = result.data;
                if (chunkData.status === 'UPLOADED' || chunkData.status === 'EXISTS') {
                    this.uploadedChunks.add(chunkNumber);
                    return true;
                }
            }
            return false;
        } catch (error) {
            console.error(`分片 ${chunkNumber} 上传失败:`, error);
            return false;
        }
    }

    /**
     * 完成上传
     */
    async _completeUpload() {
        this._stopProgressPolling();

        const response = await fetch(`${this.config.baseUrl}${this.config.apiPrefix}/complete`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({ uploadId: this.currentUploadId })
        });

        const result = await response.json();
        if (result.code !== 200) {
            throw new Error(result.message);
        }

        this._resetState();
        return result.data;
    }

    /**
     * 暂停上传
     */
    pauseUpload() {
        this.isPaused = true;
        this.isUploading = false;
        this._stopProgressPolling();
        this._triggerCallback('onStatusChange', 'paused');
    }

    /**
     * 继续上传
     */
    resumeUpload() {
        if (!this.currentUploadId) return;

        this.isPaused = false;
        this.isUploading = true;
        this._startProgressPolling();
        this._triggerCallback('onStatusChange', 'uploading');
    }

    /**
     * 取消上传
     */
    async cancelUpload() {
        if (!this.currentUploadId) return;

        try {
            await fetch(`${this.config.baseUrl}${this.config.apiPrefix}/cancel/${this.currentUploadId}`, {
                method: 'DELETE'
            });
            this._resetState();
            this._triggerCallback('onStatusChange', 'cancelled');
        } catch (error) {
            console.error('取消上传失败:', error);
        }
    }

    /**
     * 重试上传
     */
    retryUpload() {
        if (!this.currentUploadId) return;
        this.resumeUpload();
    }

    /**
     * 计算文件MD5
     */
    async _calculateFileMD5(file) {
        return new Promise((resolve, reject) => {
            const spark = new SparkMD5.ArrayBuffer();
            const fileReader = new FileReader();
            const chunkSize = 2097152; // 2MB
            let currentChunk = 0;
            const chunks = Math.ceil(file.size / chunkSize);

            fileReader.onload = function (e) {
                spark.append(e.target.result);
                currentChunk++;

                if (currentChunk < chunks) {
                    loadNext();
                } else {
                    resolve(spark.end());
                }
            };

            fileReader.onerror = () => reject('MD5计算失败');

            function loadNext() {
                const start = currentChunk * chunkSize;
                const end = Math.min(start + chunkSize, file.size);
                fileReader.readAsArrayBuffer(file.slice(start, end));
            }

            loadNext();
        });
    }

    /**
     * 计算分片MD5
     */
    async _calculateChunkMD5(chunk) {
        return new Promise((resolve, reject) => {
            const spark = new SparkMD5.ArrayBuffer();
            const fileReader = new FileReader();

            fileReader.onload = (e) => {
                spark.append(e.target.result);
                resolve(spark.end());
            };

            fileReader.onerror = () => reject('分片MD5计算失败');
            fileReader.readAsArrayBuffer(chunk);
        });
    }

    /**
     * 更新进度
     */
    _updateProgress(progress, wrapper) {
        if (!wrapper) return;

        const progressFill = wrapper.querySelector('#progressFill');
        const progressText = wrapper.querySelector('#progressText');
        const uploadProgress = wrapper.querySelector('#uploadProgress');

        if (progressFill) progressFill.style.width = progress + '%';
        if (progressText) progressText.textContent = progress + '%';
        if (uploadProgress) uploadProgress.style.display = 'block';
    }

    /**
     * 更新分片进度
     */
    _updateChunkProgress(wrapper) {
        const progress = Math.round((this.uploadedChunks.size / this.totalChunks) * 100);
        const progressText = `${progress}% (${this.uploadedChunks.size}/${this.totalChunks} 分片)`;

        this._updateProgress(progress, wrapper);

        const progressTextEl = wrapper.querySelector('#progressText');
        if (progressTextEl) progressTextEl.textContent = progressText;

        this._triggerCallback('onProgress', {
            progress,
            completedChunks: this.uploadedChunks.size,
            totalChunks: this.totalChunks,
            type: 'chunk'
        });
    }

    /**
     * 更新状态
     */
    _updateStatus(status, wrapper) {
        this._triggerCallback('onStatusChange', status);

        if (!wrapper) return;

        const fileItems = wrapper.querySelectorAll('.file-item');
        fileItems.forEach(item => {
            item.className = `file-item status-${status}`;
        });
    }

    /**
     * 显示控制按钮
     */
    _showControls(wrapper, show) {
        if (!wrapper) return;

        const controls = wrapper.querySelector('#uploadControls');
        if (controls) {
            controls.style.display = show ? 'block' : 'none';
        }
    }

    /**
     * 显示结果
     */
    _showResult(data, wrapper) {
        if (!wrapper) return;

        const info = wrapper.querySelector('#uploadInfo');
        if (info) {
            info.innerHTML = `
                <strong>上传成功!</strong><br>
                文件ID: ${data.fileId}<br>
                原始文件名: ${data.originalName}<br>
                文件大小: ${data.fileSizeFormatted}<br>
                ${data.downloadUrl ? `下载链接: <a href="${data.downloadUrl}" target="_blank">点击下载</a><br>` : ''}
                ${data.previewUrl ? `预览链接: <a href="${data.previewUrl}" target="_blank">点击预览</a>` : ''}
            `;
            info.style.display = 'block';
        }
    }

    /**
     * 显示错误
     */
    _showError(message, wrapper) {
        if (!wrapper) return;

        const info = wrapper.querySelector('#uploadInfo');
        if (info) {
            info.innerHTML = `<strong style="color: #dc3545;">上传失败:</strong> ${message}`;
            info.style.display = 'block';
        }
    }

    /**
     * 开始进度轮询
     */
    _startProgressPolling() {
        if (this.progressTimer) return;

        this.progressTimer = setInterval(async () => {
            if (this.currentUploadId && this.isUploading) {
                try {
                    const response = await fetch(`${this.config.baseUrl}${this.config.apiPrefix}/progress/${this.currentUploadId}`);
                    const result = await response.json();
                    if (result.code === 200) {
                        const progressData = result.data;
                        this.uploadedChunks.clear();
                        progressData.completedChunkNumbers.forEach(num => this.uploadedChunks.add(num));
                    }
                } catch (error) {
                    // 静默处理
                }
            }
        }, this.config.progressPollingInterval);
    }

    /**
     * 停止进度轮询
     */
    _stopProgressPolling() {
        if (this.progressTimer) {
            clearInterval(this.progressTimer);
            this.progressTimer = null;
        }
    }

    /**
     * 重置状态
     */
    _resetState() {
        this.isUploading = false;
        this.isPaused = false;
        this.currentUploadId = null;
        this.currentFile = null;
        this.totalChunks = 0;
        this.uploadedChunks.clear();
        this.chunks = [];
        this.currentChunkIndex = 0;
        this._stopProgressPolling();
    }

    /**
     * 触发回调
     */
    _triggerCallback(callbackName, data) {
        const callback = this.config[callbackName];
        if (typeof callback === 'function') {
            try {
                callback(data);
            } catch (error) {
                console.error(`回调 ${callbackName} 执行失败:`, error);
            }
        }
    }

    /**
     * 格式化文件大小
     */
    _formatFileSize(bytes) {
        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
        if (bytes === 0) return '0 Bytes';
        const i = Math.floor(Math.log(bytes) / Math.log(1024));
        return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
    }
}

// 导出组件
if (typeof module !== 'undefined' && module.exports) {
    module.exports = FileUploadComponent;
} else if (typeof window !== 'undefined') {
    window.FileUploadComponent = FileUploadComponent;
} 